package com.me.collector;

import java.util.ArrayList;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool.PooledEffect;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.me.collector.Definitions.EntityCategory;

public class Hero {
	
    static final float WIDTH = 64;
    static final float HEIGHT = 64;
    static final float maxSpeed = 1.6f; // maximal X-Speed
    static final float dullness = 3f; // makes the hero accelerate. ATTENTION slowing down is managed by the body's friction
    static final float ANIM_WALK = 0.07f;  // walking animation speed
    static final float MAXSTEP = 0.2f;
	
    Body body;
    Texture texture;
	TextureRegion region;
	Sprite sprite;
	Container container;
	//Container secondContainer;
	Animation animation;
	AnimationType currentAnimation;
	Sound soundStep;
	ParticleEffectPool dustEffectPool;
    Array<PooledEffect> effects;
	float animationTime;
	float stepTime;
	short category = (short)EntityCategory.Hero.getValue();
	short mask = (short)((short)EntityCategory.Ground.getValue() |
			(short)EntityCategory.Item.getValue() |
			(short)EntityCategory.Static.getValue());
	
	// Order is important. first Type will be the topmost animation etc.
	enum AnimationType {
		standing,
		walkingLeft,
		walkingRight
	}
	
	public Hero(World world, float x, float y) {
		// Body
		BodyDef def = new BodyDef();
		def.type = BodyType.DynamicBody;
		def.position.set(x * Definitions.WORLD_TO_BOX, y * Definitions.WORLD_TO_BOX);
		def.fixedRotation = true;
		
		body = world.createBody(def);
		
		PolygonShape shape = new PolygonShape();
		shape.setAsBox((WIDTH / 2) * Definitions.WORLD_TO_BOX / 2f, HEIGHT * Definitions.WORLD_TO_BOX / 2f, new Vector2((WIDTH / 2) * Definitions.WORLD_TO_BOX / 2f, HEIGHT * Definitions.WORLD_TO_BOX / 2f), 0);
		
		FixtureDef fixtureDef = new FixtureDef();
		fixtureDef.shape = shape;
		fixtureDef.density = 3f;
		fixtureDef.friction = 15f;
		fixtureDef.restitution = 0f;
		fixtureDef.filter.categoryBits = category;
		fixtureDef.filter.maskBits = mask;
		body.createFixture(fixtureDef);
		shape.dispose();
		
		// Sprite
		texture = new Texture(Gdx.files.internal("hero/hero.png"));
		region = new TextureRegion(texture);
		setAnimation(AnimationType.standing, 0.5f);
		
		sprite = new Sprite();
		sprite.setSize(WIDTH, HEIGHT);
		sprite.setOrigin(0, 0);
		sprite.setPosition(body.getPosition().x * Definitions.BOX_TO_WORLD - sprite.getWidth() / 4f, body.getPosition().y * Definitions.BOX_TO_WORLD);
		
		// Container
		container = new Container(world, sprite, body, sprite.getWidth() / 2f);
		
		// Sounds
		soundStep = Gdx.audio.newSound(Gdx.files.internal("sounds/step.wav"));
		
		// Particles
		effects = new Array();
	    ParticleEffect dustEffect = new ParticleEffect();
	    dustEffect.load(Gdx.files.internal("particles/dustParticle.p"), Gdx.files.internal("particles"));
	    dustEffectPool = new ParticleEffectPool(dustEffect, 1, 2);
	}
	
	
	public void update() {
		// Animation
		animationTime += Definitions.BOX_STEP;
		sprite.setRegion(animation.getKeyFrame(animationTime, true));
		if(Math.abs(body.getLinearVelocity().x) < 0.2f) {
			if(currentAnimation != AnimationType.standing) setAnimation(AnimationType.standing, 0.5f);
		}
		else if(body.getLinearVelocity().x < 0f) {
			if(currentAnimation != AnimationType.walkingLeft) setAnimation(AnimationType.walkingLeft, ANIM_WALK);
		}
		else if(body.getLinearVelocity().x > 0f) {
			if(currentAnimation != AnimationType.walkingRight) setAnimation(AnimationType.walkingRight, ANIM_WALK);
		}
		// Step Sound
		if(currentAnimation == AnimationType.walkingLeft || currentAnimation == AnimationType.walkingRight) {
			stepTime += Definitions.BOX_STEP;
			if(stepTime > MAXSTEP) {
				stepTime = 0;
				soundStep.play();
				addDirtEffect(sprite.getX() + 0.5f * sprite.getWidth(), sprite.getY() + sprite.getHeight() / 20f);
			}
		}
		sprite.setPosition(body.getPosition().x * Definitions.BOX_TO_WORLD - sprite.getWidth() / 4f, body.getPosition().y * Definitions.BOX_TO_WORLD);
		container.update();
		getInput();
	}
	
	// creates a dust effect at the position x|y
	private void addDirtEffect(float x, float y) {
		PooledEffect effect = dustEffectPool.obtain();
		effect.setPosition(x, y);
		effects.add(effect);
	}
	
	// draws the dust particles
	private void drawParticles(SpriteBatch batch, float delta) {
		for (int i = effects.size - 1; i >= 0; i--) {
	        PooledEffect effect = effects.get(i);
	        effect.draw(batch, delta);
	        if (effect.isComplete()) {
	                effect.free();
	                effects.removeIndex(i);
	        }
		}
	}
	
	// Sets the Heros movment from different input types
	private void getInput() {
		getDesktopInput();
		//getPitchInput(); // Pitch Input is not easy! better use Touch ?
		getTouchInput();
	}
	
	// Sets the Heros movement from key input
	private void getDesktopInput() {
		if(Gdx.input.isKeyPressed(Keys.LEFT))
			body.setLinearVelocity(body.getLinearVelocity().x - 1 / dullness, body.getLinearVelocity().y);
		else if(Gdx.input.isKeyPressed(Keys.RIGHT))
			body.setLinearVelocity(body.getLinearVelocity().x + 1 / dullness, body.getLinearVelocity().y);
		if(Math.abs(body.getLinearVelocity().x) > maxSpeed) body.setLinearVelocity(body.getLinearVelocity().x / Math.abs(body.getLinearVelocity().x) * maxSpeed, body.getLinearVelocity().y);
	}
	
	// Sets the Heros movement from the phones rotation
	private void getPitchInput() {
		float angle;
    	if(Math.abs(Gdx.input.getPitch()) > 2) {
    		angle = Gdx.input.getPitch();
    		//if(Math.abs(angle) < 10) angle = (angle / Math.abs(angle)) * 10;
    		if(Math.abs(angle) > 45) angle = (angle / Math.abs(angle)) * 45;
    		body.setLinearVelocity(body.getLinearVelocity().x - angle / (dullness * 45), body.getLinearVelocity().y);
    	}
    	if(Math.abs(body.getLinearVelocity().x) > maxSpeed) body.setLinearVelocity(body.getLinearVelocity().x / Math.abs(body.getLinearVelocity().x) * maxSpeed, body.getLinearVelocity().y);
	}
	
	// Sets the Heros movement from the phones touchscreen
	private void getTouchInput() {
		int index = 0;
		if(Gdx.input.isTouched(0) || Gdx.input.isTouched(++index)) {
			if(Gdx.input.getX(index) < Gdx.graphics.getWidth() / 2f) {
				// Left
				body.setLinearVelocity(body.getLinearVelocity().x - 1 / dullness, body.getLinearVelocity().y);
			}
			else {
				// Right
				body.setLinearVelocity(body.getLinearVelocity().x + 1 / dullness, body.getLinearVelocity().y);
			}
		}
		if(Math.abs(body.getLinearVelocity().x) > maxSpeed) body.setLinearVelocity(body.getLinearVelocity().x / Math.abs(body.getLinearVelocity().x) * maxSpeed, body.getLinearVelocity().y);
	}
	
	// Sets the current animation using AnimationType enum
	public void setAnimation(AnimationType animationType, float time) {
		animationTime = 0;
		animation = new Animation(time, region.split(64, 64)[animationType.ordinal()]);
		currentAnimation = animationType;
	}
	
	// Draws hero and his container(s)
	public void draw(SpriteBatch batch) {
		sprite.draw(batch);
		container.draw(batch);
		drawParticles(batch, Definitions.BOX_STEP);
	}
	
	// sets resources free
	public void dispose() {
		for (int i = effects.size - 1; i >= 0; i--)
		    effects.get(i).free();
		effects.clear();
		texture.dispose();
	}
	
	public void pause()
	{
		body.setAwake(false);
		container.pause();
	}
	
	public void resume()
	{
		body.setAwake(true);
		container.resume();
	}
	
	// properties
	public Body getBody()
	{
		return body;
	}
	
	public Container getContainer()
	{
		return container;
	}
}
